package com.wquick.base.compose

import android.util.Log
import android.widget.FrameLayout
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.viewinterop.AndroidView
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentOnAttachListener
import androidx.fragment.app.findFragment
import java.lang.ref.WeakReference

/**
 * Make fragment as Composable by AndroidView
 *
 * @param fragment fragment
 * @param fm add fragment by FragmentManager, can be childFragmentManager
 * @param update The callback to be invoked after the layout is inflated.
 */
@Composable
fun <T : Fragment> FragmentComposable(
    modifier: Modifier = Modifier,
    fragment: T,
    update: (T) -> Unit = {}
) {
    val localView = LocalView.current
    // Find the parent fragment, if one exists. This will let us ensure that
    // fragments inflated via a FragmentContainerView are properly nested
    // (which, in turn, allows the fragments to properly save/restore their state)
    val parentFragment = remember(localView) {
        try {
            localView.findFragment<Fragment>().takeIf { it.isAdded }
        } catch (e: IllegalStateException) {
            // findFragment throws if no parent fragment is found
            null
        }
    }

    val fragments = remember { mutableListOf<WeakReference<Fragment>>() }

    val attachListener = remember {
        FragmentOnAttachListener { _, fragment ->
            Log.d("FragmentComposable", "fragment: $fragment")
            fragments += WeakReference(fragment)
        }
    }

    val localContext = LocalContext.current

    DisposableEffect(localContext) {
        val fragmentManager = parentFragment?.childFragmentManager
            ?: (localContext as? FragmentActivity)?.supportFragmentManager
        fragmentManager?.addFragmentOnAttachListener(attachListener)

        onDispose {
            fragmentManager?.removeFragmentOnAttachListener(attachListener)
            if (fragmentManager?.isStateSaved == false) {
                fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
                fragments
                    .filter { it.get()?.isRemoving == false }
                    .reversed()
                    .forEach { existingFragment ->
                        Log.d("FragmentComposable", "remove:${existingFragment.get()}")
                        fragmentManager
                            .beginTransaction()
                            .remove(existingFragment.get()!!)
                            .commitAllowingStateLoss()
                    }
            }
        }
    }

    AndroidView(
        modifier = modifier,
        factory = { context ->
            FrameLayout(context).apply {
                id = System.currentTimeMillis().toInt()
                require(!fragment.isAdded) { "$fragment must not attach to any host" }
                val fragmentManager = parentFragment?.childFragmentManager
                    ?: (localContext as? FragmentActivity)?.supportFragmentManager
                fragmentManager
                    ?.beginTransaction()
                    ?.setReorderingAllowed(true)
                    ?.replace(this.id, fragment, fragment.javaClass.name)
                    ?.commitAllowingStateLoss()
                fragments.clear()
            }
        },
        update = { update(fragment) }
    )
}
